package tinyinventor.compiler;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import renderable.RenderableBlock;
import workspace.Page;
import codeblocks.Block;

/**
 * The Compiler takes care of compiling the Block's created by the user into actual code.
 * A Compiler represent each Block as a Component which know how the specific Block is to
 * compiled. Available Components are defined in XML and loaded into the Compiler's
 * prototypeComponent list.
 * 
 * @author Morten Tranberg Hansen
 *
 */

public abstract class Compiler {
	
	public static final String DECLARATION_STRING = "/** DO NOT EDIT. THIS FILE IS AUTOMATICALLY GENERATED WITH TINY INVENTOR**/";
	
	List<CompileCheck> checks;
	
	public Compiler() {
		checks = new LinkedList<CompileCheck>();
	}
	
	public void addCheck(CompileCheck check) {
		checks.add(check);
	}
	
	/**
	 * Compile the current program represented by Block's in the Workspace.
	 * 
	 * @throws CompileException
	 */
	public void compilePage(Page page) throws CompileException {
		//for(Page page : Workspace.getInstance().getBlockCanvas().getPages()) {
		Collection<RenderableBlock> blocks = page.getTopLevelBlocks();
		for(RenderableBlock rb : blocks) {
			Block block = rb.getBlock();
			Component c = getComponent(block);
			c.compile(this, block);
		}
		
		for(CompileCheck check : checks) {
			check.valid();
		}
	}
	
	/**
	 * Get a component with a certain name based on the prototype components loaded.
	 * The component can optionally be related to a block. If the the component does not
	 * already exist is it automatically created.
	 * @param name the name of the component
	 * @param block the optional block which the component is related to
	 * @return the component
	 * @throws CompileException if the corresponding prototype Component cannot be found
	 */
	protected abstract Component getComponent(Block block) throws CompileException;
		
	public void build(String path) throws IOException {
		File file = new File(path);
		if(!file.exists()) {
			file.mkdirs();
		}
	}
	
	/**
	 * Cleans a string for use in code. This replaces hyphens.
	 * 
	 * @param string the string to be cleaned
	 * @return
	 */
	protected static String cleanString(String string) {
		return string.replaceAll("-", "_");
	}

	/**
	 * Helper method which applies indentation to some code in a file.
	 * TODO: this function is not too flexible with the style of code to indent
	 * @param file
	 * @throws IOException
	 */
	protected static void indent(File file) throws IOException {
		BufferedReader reader = new BufferedReader(new FileReader(file));
		StringBuilder result = new StringBuilder();
		String line;
		int indent = 0;
		while((line=reader.readLine())!=null) {
			line = line.trim();
			if(line.startsWith("}") || line.startsWith("#}")) indent--;
			for(int i=0; i<indent; i++) {
				result.append("\t");
			}
			result.append(line);
			result.append("\n");
			//indend += line.replaceAll("[^{]", "").length() - line.replaceAll("[^}]", "").length();
			if(line.endsWith("{")) indent++;
			//System.out.println("indend: " + indend);
		}
		reader.close();
		BufferedWriter writer = new BufferedWriter(new FileWriter(file));
		writer.write(result.toString());
		writer.close();
	}
	
}
